package org.yunai.swjg.server.module.player;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.springframework.stereotype.Service;
import org.yunai.swjg.server.core.annotation.MainThread;
import org.yunai.swjg.server.core.service.Online;
import org.yunai.swjg.server.core.service.OnlineContextService;
import org.yunai.swjg.server.core.service.OnlineState;
import org.yunai.swjg.server.module.player.operation.PlayerLoadOperation;
import org.yunai.swjg.server.module.player.operation.PlayerSaveOperation;
import org.yunai.swjg.server.module.player.vo.Player;
import org.yunai.swjg.server.module.rep.RepSceneService;
import org.yunai.swjg.server.module.rep.callback.PlayerOfflineRepSceneCallback;
import org.yunai.swjg.server.module.scene.NormalSceneService;
import org.yunai.swjg.server.module.scene.callback.PlayerOfflineNormalSceneCallback;
import org.yunai.yfserver.async.IoOperationService;
import org.yunai.yfserver.common.LoggerFactory;

import javax.annotation.Resource;
import java.util.Collection;

/**
 * 玩家Service
 * User: yunai
 * Date: 13-4-5
 * Time: 下午4:30
 */
@Service
public class PlayerService {

    private static final Logger LOGGER_GAME = LoggerFactory.getLogger(LoggerFactory.Logger.game, PlayerService.class);

    @Resource
    private IoOperationService ioOperationService;
    @Resource
    private OnlineContextService onlineContextService;
    @Resource
    private NormalSceneService normalSceneService;
    @Resource
    private RepSceneService repSceneService;

    /**
     * 玩家信息加载
     *
     * @param online 在线信息
     */
    public void onPlayerLoad(Online online) {
        // 设置[在线状态]为[OnlineState.authed]
        online.setState(OnlineState.authed);
        onlineContextService.addUser(online);
        // 异步加载角色信息
        ioOperationService.asyncExecute(new PlayerLoadOperation(online));
    }

    /**
     * 当服务器关闭时，将所有玩家下线<br />
     * <b>该方法请不要随便调用</b>
     */
    public void offAllPlayersLogoutWhenServerShutDown() {
        Collection<Online> onlines = onlineContextService.getPlayers();
        LOGGER_GAME.info("[offAllPlayersLogoutWhenServerShutDown] [size:{}].", onlines.size());
        for (Online online : onlines) {
            if (online == null) {
                LOGGER_GAME.error("[offAllPlayersLogoutWhenServerShutDown] [online is null].");
                continue;
            }
            if (online.getState() == OnlineState.logouting) {
                // TEST: 极端情况时，玩家普通退出时候，在Scene中，这个时候刚好服务器关闭心跳进程，导致退出无法完成。
                LOGGER_GAME.error("[offAllPlayersLogoutWhenServerShutDown] [online({}).state is logouting].", online.getPlayer().getId());
            }
            online.setExitReason(PlayerExitReason.SERVER_SHUTDOWN);
            try {
                onPlayerLogout(online);
            } catch (Exception e) {
                LOGGER_GAME.error("[offAllPlayersLogoutWhenServerShutDown] [online{} offline error:{}].",
                        online.getPlayer().getId(), ExceptionUtils.getStackTrace(e));
            }
        }
    }

    /**
     * 玩家退出
     *
     * @param online 在线信息
     */
    public void onPlayerLogout(Online online) {
        // 设置下线原因
        if (online.getExitReason() == null) {
            online.setExitReason(PlayerExitReason.LOGOUT);
        }

        // 有几个状态无须入库操作可以直接退出
        switch (online.getState()) {
            case connected:
            case temp_authed:
            case client_create_roleing:
            case client_create_roled:
            case load_roleing:
                online.setState(OnlineState.logouting);
                onlineContextService.removeOnline(online.getSession());
                if (online.getUser() != null) {
                    onlineContextService.removeUser(online);
                    if (online.getPlayer() != null) {
                        onlineContextService.removePlayer(online);
                    }
                }
                online.setState(OnlineState.logouted);
                return;
        }

        // 需要入库的操作
        assert online.getExitReason() != null;
        switch (online.getExitReason()) {
            case SERVER_SHUTDOWN: // 停机
                if (online.isInRepScene() || online.isInActivityRepScene()) {
                    repSceneService.gotoBeforeScene(online);
                }
                online.setState(OnlineState.logouting);
                ioOperationService.syncExecute(new PlayerSaveOperation(online)); // 同步入库（执行该方法时，各个服务已经关闭，不会出现并行的情况了）
                break;
            case MULTI_LOGIN: // 多点登录
                if (online.isInNormalScene()) {
                    online.setState(OnlineState.logouting);
                    normalSceneService.onPlayerLeaveScene(online, new PlayerOfflineNormalSceneCallback(online.getPlayer().getId()));
                } else if (online.isInRepScene() || online.isInActivityRepScene()) {
                    online.setState(OnlineState.logouting);
                    repSceneService.onPlayerLeaveScene(online, new PlayerOfflineRepSceneCallback(online.getPlayer().getId()));
                } else {
                    PlayerSaveOperation saveOperation = new PlayerSaveOperation(online);
                    ioOperationService.asyncExecute(saveOperation); // 异步入库(由于，加载用户详细信息是串行异步任务，串行会使得入库完成后，才执行加载玩家数据，因此，数据不会有问题）
                    Online willAuthOnline = onlineContextService.removeWillAuthUser(online.getUser().getId());
                    if (willAuthOnline != null && willAuthOnline.isConnected()) {
                        onPlayerLoad(willAuthOnline);
                    }
                }
                break;
            default:
                if (online.isInNormalScene()) {
                    online.setState(OnlineState.logouting);
                    normalSceneService.onPlayerLeaveScene(online, new PlayerOfflineNormalSceneCallback(online.getPlayer().getId()));
                } else if (online.isInRepScene() || online.isInActivityRepScene()) {
                    online.setState(OnlineState.logouting);
                    repSceneService.onPlayerLeaveScene(online, new PlayerOfflineRepSceneCallback(online.getPlayer().getId()));
                } else {
                    PlayerSaveOperation saveOperation = new PlayerSaveOperation(online); // 异步入库(由于，加载用户详细信息是串行异步任务，串行会使得入库完成后，才执行加载玩家数据，因此，数据不会有问题）
                    ioOperationService.asyncExecute(saveOperation);
                }
        }
    }

    /**
     * 玩家进入其所在场景
     *
     * @param online 在线信息
     */
    @MainThread
    public void enterScene(Online online) {
        // 加载角色后设置状态为进入场景
        if (online.getState() == OnlineState.load_roled) {
            online.setState(OnlineState.enter_sceneing);
        }
        // 进入场景
        Player player = online.getPlayer();
        if (!normalSceneService.onPlayerEnterNormalScene(online, player.getSceneId(), player.getSceneX(), player.getSceneY())) {
            online.setExitReason(PlayerExitReason.SERVER_ERROR);
            online.disconnect();
        }
    }

}
